/**
 * This draws the panel above the contig display and pairs alignment display
 * but below the Show EST Alignment  << Prev >> Next - which are done in ClusterViewTab
 */
package jpave.query_interface;

import javax.swing.JOptionPane;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.Transferable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.TreeSet;
import java.util.Vector;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;

import jpave.database.JPaveDBWrapper;
import jpave.dataholders.GlobalSettings;
import jpave.dataholders.SequenceData;
import jpave.dataholders.MainData;
import jpave.dataholders.ContigData;
import jpave.dataholders.AlignmentData;
import jpave.query_interface.JPaveFrame;
import util.Debug.ErrorReport;
import util.methods.UIHelpers;
import util.ui.MenuMapper;


public class MainToolBarPanel extends JPanel implements ClipboardOwner 
{		
	public static int HIDE_BURIED_EST = 0;
	public static int SHOW_BURIED_EST_LOCATION = 1;
	public static int SHOW_BURIED_EST_DETAIL = 2;
	
	// Needed for clipboard class
	public void lostOwnership(Clipboard clipboard, Transferable contents) { }
	
	// panel for EST alignment
	static MainToolBarPanel createESTPanel ( GlobalSettings inSettings,
					MainData inCluster, int inRecordNum, int parentID,
					boolean hasNoAssembly) throws Exception 
	{
		// Create the list of sub-panels 
		Vector<MainContigAlignPanel> subPanels = new Vector<MainContigAlignPanel> ();
		Iterator<ContigData> i = inCluster.getContigIterator();
		int nTotalESTs = 0;		
		int nTotalBuried = 0; 	
		isPair = false;

		while ( i.hasNext() )
		{
			// Create the panel and add it to parent panel and list
			ContigData curData = (ContigData)i.next ();
			MainContigAlignPanel curPanel = new MainContigAlignPanel (curData, 													
								nTopGap, nBottomGap, nSideGaps, nSideGaps );
			subPanels.add( curPanel );	
			
			nTotalESTs += curData.getNumSequences();	
			nTotalBuried += curData.getNumBuried(); 		
		}

		// Create the scroll panel with our alignment panels
		MainToolBarPanel panel = new MainToolBarPanel ( 	inSettings, 
									inRecordNum, parentID);
		panel.createMainPanelFromSubPanels( subPanels, !hasNoAssembly );
		
		// Install the toolbars and scroller
		if (hasNoAssembly) { 
			panel.add ( panel.createSeqToolbar() );
		}
		else {
			panel.add ( panel.createESTToolbar( nTotalESTs, nTotalBuried) );
		}
		panel.add ( Box.createVerticalStrut(5) );
		panel.add ( panel.scroller );
		
		if (!hasNoAssembly) {
			panel.menuViewType.setSelectedIndex( 2 );
		}
		return panel;
	}

	// MainTopRowTab.installPairwiseAlignmentPanel panel for Pairwise alignment
	static MainToolBarPanel createPairwisePanel ( GlobalSettings inSettings, 
						Vector<AlignmentData> inAlignmentLists) 
	{
		isPair = true;
		// Create the list of sub-panels 
		Vector<MainPairAlignPanel> subPanels = new Vector<MainPairAlignPanel> ();
		Iterator<AlignmentData> i = inAlignmentLists.iterator();
		String lastCtg2 = "";
		boolean alt = true;
		int cnt=1;
		while ( i.hasNext() )
		{
			AlignmentData alignData = (AlignmentData)i.next ();
			String hitID = alignData.getSequence2().getName();
			if (!lastCtg2.equals(hitID)) { // display in alternating colors
				alt = !alt;
				lastCtg2 = hitID;
			}
			MainPairAlignPanel curPanel = new MainPairAlignPanel ( 
					cnt, alignData,
					alt, nTopGap, nBottomGap, nSideGaps, nSideGaps );
			subPanels.add( curPanel );	
			cnt++;
		}
		
		MainToolBarPanel panel = new MainToolBarPanel ( inSettings, -1, -1 );
		panel.createMainPanelFromSubPanels( subPanels, true );
		
		panel.add ( panel.createPairwiseToolbar ( false ) );
		panel.add ( Box.createVerticalStrut(5) );
		panel.add ( panel.scroller );	
		return panel;
	}	
	
	private MainToolBarPanel ( GlobalSettings inSettings, 
							int inRecordNum, int parentID) 
	{
        theSettings = inSettings;
		nRecordNum = inRecordNum; 
		parentTabID = parentID;
		
		scroller = new JScrollPane ( );
		scroller.setHorizontalScrollBarPolicy( JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED );
		scroller.setVerticalScrollBarPolicy( JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED );
		scroller.addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent e) {
				handleClick(e);
			}
		});	
        UIHelpers.setScrollIncrements( scroller );
        
		scroller.addComponentListener(new ComponentAdapter() {
			public void componentResized(ComponentEvent e) {
				// Now that the window has been layed out
				fitBasesPerPixel();
				scroller.removeComponentListener(this);
			}
		});
        
		setLayout( new BoxLayout ( this, BoxLayout.Y_AXIS ) );
		super.setBackground(Color.WHITE);
	}
	
	private void createMainPanelFromSubPanels ( Vector inPanels, boolean hasAssembly )
	{
		JPanel mainPanel = new JPanel ( );
		mainPanel.setLayout( new BoxLayout ( mainPanel, BoxLayout.Y_AXIS ) );
		mainPanel.setBackground( Color.WHITE );
		mainPanel.setVisible( false );
		
		// Add each panel to the parent panel
		listOfAlignmentPanels = new Vector<MainAlignPanel> ();
		Iterator iter = inPanels.iterator();
		while ( iter.hasNext() )
		{
			MainAlignPanel curPanel = (MainAlignPanel)iter.next ();
			mainPanel.add( curPanel );
			listOfAlignmentPanels.add( curPanel );
			if ( curPanel instanceof MainContigAlignPanel ) {
				MainContigAlignPanel polyMorpy = (MainContigAlignPanel)curPanel; 
				polyMorpy.setClusterPanel ( this );
			}
		}

		mainPanel.add( Box.createVerticalStrut(30) );
		mainPanel.add( createLegendPanel(hasAssembly) );
		mainPanel.add( Box.createVerticalStrut(30) );
		mainPanel.add( Box.createVerticalGlue() ); 	
		
		scroller.setViewportView( mainPanel );
	}

	private JPanel createLegendPanel(boolean hasAssembly)
	{
		JPanel panel = new JPanel();
		panel.setBackground( Color.WHITE );
		panel.setLayout( new BoxLayout ( panel, BoxLayout.X_AXIS ) );
		panel.add( Box.createHorizontalStrut(10) );
		
		// the legend is different for pairwise vs contig
		LegendPanel lp;
		if (!isPair && !hasAssembly) lp = new LegendPanel(3);
		else if (isPair) lp = new LegendPanel(2);
		else lp = new LegendPanel(1);		
		lp.setIsPair(isPair);
		lp.setHasNoAssembly(!hasAssembly);
		
		panel.add( lp );
		panel.add( Box.createHorizontalGlue() );
		return panel;
	}
	
	public void addShowHideListener(ActionListener al)
	{
		if(menuShowBuried != null)
			menuShowBuried.addActionListener(al);
	}

	private JPanel createESTToolbar ( final int nESTs, final int nBuried)
	{	
		// Show SNPs
		btnViewSNPs = new JButton ("Show SNPs");
		btnViewSNPs.addActionListener(new ActionListener() {
			boolean bShowSNPS = false;
			boolean bDoneCalc = false;

			// on-the-fly calculation thread 
			public void actionPerformed(ActionEvent e) {
				bShowSNPS = !bShowSNPS;
				
				if (bShowSNPS) {
					if (!bDoneCalc && nESTs >= 100) {
						btnViewSNPs.setText("Calculating SNPs ...");
						btnViewSNPs.setEnabled(false);
					}
					Thread theThread = new Thread(new Runnable() {
						public void run() {	
							setShowSNPs(bShowSNPS);
							bDoneCalc = true;
							btnViewSNPs.setText("Hide SNPs");
							btnViewSNPs.setEnabled(true);
						}
					});
					theThread.setPriority(Thread.MIN_PRIORITY);
					theThread.start();
				}
				else {
					setShowSNPs(bShowSNPS);
					btnViewSNPs.setText("Show SNPs");
				}
			}
		});	

		menuShowBuried = new JComboBox ();
		menuShowBuried.addItem("Hide Buried");
		menuShowBuried.addItem("Show Buried (Location)");
		menuShowBuried.addItem("Show Buried (Detail)");
		menuShowBuried.setBackground(Color.WHITE);

		JLabel lblFind = new JLabel("Find EST: ");
		final JTextField txtFind  = new JTextField(15);
		txtFind.addCaretListener(new CaretListener() {
			public void caretUpdate(CaretEvent e) {
				setSelectedClones(txtFind.getText());
			}
		});
		txtFind.setMaximumSize(txtFind.getPreferredSize());

		// Sort menu
		Dimension dim = new Dimension ( (int)(btnViewSNPs.getPreferredSize().getWidth() * 3.0 ), 
											(int)btnViewSNPs.getPreferredSize().getHeight() );
		menuViewType = new JComboBox ();
		menuViewType.setPreferredSize( dim );
		menuViewType.setMaximumSize ( dim );
		menuViewType.addItem( new MenuMapper ( "Sort sequences by Name", 
												ContigData.SORT_BY_NAME ) );
		menuViewType.addItem( new MenuMapper ( "Sort sequences by Left Position", 
												ContigData.SORT_BY_LEFT_POS ) );
		menuViewType.addItem( new MenuMapper ( "Sort sequences by Left Position; Group F/R Pairs", 
												ContigData.SORT_BY_GROUPED_LEFT_POS ) );
		menuViewType.setBackground(Color.WHITE);
		menuViewType.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				changeSortOrder(menuViewType.getSelectedIndex());
				revalidate(); 
			}
		});
		
		boolean doSelect=false;
		if (theSettings.canCallCAP3()) { // Cap
			doSelect = true;
			btnCAP3 = new JButton("Run CAP3");
			btnCAP3.setToolTipText("Execute CAP3 on selected ESTs");
			btnCAP3.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					alignSelectedESTs(/*AppletToServlet.SERVER_COMMAND_CAP*/ "cap3"); 
				}
			});
		}
		
		if (theSettings.canCallPHRAP()) {
			doSelect = true;
			btnPHRAP = new JButton("Run PHRAP");
			btnPHRAP.setToolTipText("Execute PHRAP on selected ESTs");
			btnPHRAP.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					alignSelectedESTs(/*AppletToServlet.SERVER_COMMAND_PHRAP*/ "phrap"); 
				}
			});
		}
		
		//Replace library EST with current view
		btnReplace = new JButton("Replace");
		btnReplace.setToolTipText("Replace original contig with this contig");
		btnReplace.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				MainData replaceCluster = MainData.loadFromFile(CAP3orPhrapTab.GetResult(parentTabID));
				if(JOptionPane.showConfirmDialog(
						getParent(), "Replace "+ 
						replaceCluster.getSourceName(), "Confirm Replace", JOptionPane.YES_NO_OPTION)==JOptionPane.YES_OPTION)
				{
				Connection inConnection = null;
				try {
					inConnection = theSettings.getSelectedSession().getAssembly(0).getDBData().createDBConnection();
				} catch (Exception e1) {
					ErrorReport.reportError(e1,"Internal error: confirm replace");
				}
				JPaveDBWrapper.replaceContig(inConnection, replaceCluster.getContigAt(0), 0);
				
				try {
					inConnection.close();
				} catch (SQLException e1) {
					ErrorReport.reportError(e1,"Internal SQL error: confirm replace");
				}
				//TODO - Close CAP results/original contig - reload contig
				RemoveAfterCAPReplace(replaceCluster.getSourceName());
				}
			}
		});

        // Button panel
		btnViewType = new JButton ("View Bases");
		btnViewType.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				toggleViewType();
			}
		});
		
		menuShowCoding = new JComboBox ();
		menuShowCoding.setPreferredSize( dim );
		menuShowCoding.setMaximumSize ( dim );
		menuShowCoding.addItem( new MenuMapper ( "Don't show coding region", SHOW_CODING_NONE ) );
		menuShowCoding.addItem( new MenuMapper ( "Show longest ORF", SHOW_CODING_ORF ) );
		menuShowCoding.addItem( new MenuMapper ( "Show protein aligned ORF", SHOW_CODING_PROTEIN ) );
		menuShowCoding.setBackground(Color.WHITE);
		menuShowCoding.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				MenuMapper selection = (MenuMapper) menuShowCoding.getSelectedItem();
				changeShowCoding(selection.asInt());
			}
		});		
		
		Dimension dim2 = new Dimension ( (int)(btnViewType.getPreferredSize().getWidth() * 1.5), 
											(int)btnViewType.getPreferredSize().getHeight() );
		menuHorzRatio = new JComboBox ();
		menuHorzRatio.setPreferredSize( dim2 );
		menuHorzRatio.setMaximumSize ( dim2 );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:1", 1 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:2", 2 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:3", 3 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:4", 4 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:5", 5 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:6", 6 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:7", 7 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:8", 8 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:9", 9 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:10", 10 ) );
		menuHorzRatio.setBackground(Color.WHITE);
		menuHorzRatio.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					MenuMapper selection = (MenuMapper) menuHorzRatio.getSelectedItem();
					changeBasesPerPixel(selection.asInt());
				} catch (Exception err) {
					ErrorReport.reportError(err);
				}
				revalidate(); 
			}
		});		
		
		JButton btnClusterSelectNone = null;
		JButton btnClusterSelectAll = null;
			
		// Add panel with Select All/Select None buttons:
		if (doSelect) {
			btnClusterSelectAll = new JButton ( "Select All" );
			btnClusterSelectAll.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					selectAll();
				}
			});
			
			btnClusterSelectNone = new JButton ( "Unselect All" ); 
			btnClusterSelectNone.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					selectNone();
				}
			});
		}
		// 2nd row of buttons
		JPanel topPanel1 = new JPanel ( );
		topPanel1.setLayout( new BoxLayout ( topPanel1, BoxLayout.X_AXIS ) );
		topPanel1.add( Box.createHorizontalStrut(5) );
		topPanel1.add( lblFind ); 						
		topPanel1.add( txtFind ); 						
		topPanel1.add( Box.createHorizontalStrut(5) ); 
		if (btnClusterSelectAll!=null) {
			topPanel1.add( btnClusterSelectAll );
			topPanel1.add( Box.createHorizontalStrut(5) ); 
			topPanel1.add( btnClusterSelectNone );
			topPanel1.add( Box.createHorizontalStrut(5) ); 
		}

		if ( btnCAP3 != null ) {
			topPanel1.add( btnCAP3 );
			topPanel1.add( Box.createHorizontalStrut(5) );
		}
		if ( btnPHRAP != null ) {		
			topPanel1.add( btnPHRAP );
			topPanel1.add( Box.createHorizontalStrut(5) );
		}
		topPanel1.add( Box.createHorizontalGlue() );
		topPanel1.add( Box.createHorizontalStrut(5) );	
		topPanel1.setBackground(Color.WHITE);
		
		// third row of button
		JPanel topPanel2 = new JPanel();
		topPanel2.setLayout( new BoxLayout ( topPanel2, BoxLayout.X_AXIS ) );
		topPanel2.add( Box.createHorizontalStrut(5) );
		topPanel2.add( menuViewType );
		topPanel2.add( Box.createHorizontalStrut(5) ); 
		topPanel2.add( btnViewSNPs );
		topPanel2.add( Box.createHorizontalStrut(5) );
		
		// FIXME ORFBUG reverse is not working
		//topPanel2.add( menuShowCoding );
		//topPanel2.add( Box.createHorizontalStrut(5) );	
		topPanel2.add( Box.createHorizontalGlue() );
		topPanel2.setBackground(Color.WHITE);
		
		// fourth row of buttons
		JPanel topPanel3 = new JPanel();
		topPanel3.setLayout( new BoxLayout ( topPanel3, BoxLayout.X_AXIS ) );
		topPanel3.add( Box.createHorizontalStrut(5) );			
		topPanel3.add(btnViewType);
		topPanel3.add( Box.createHorizontalStrut(5) );
		topPanel3.add(menuHorzRatio);
		topPanel3.add( Box.createHorizontalStrut(5) );
		topPanel3.add( menuShowBuried ); 				
		topPanel3.add( Box.createHorizontalStrut(5) );
		if ( btnReplace != null) {
			topPanel3.add(btnReplace);
			topPanel3.add( Box.createHorizontalStrut(5) );
		}
		
		topPanel3.add( Box.createHorizontalGlue() );
		topPanel3.setBackground(Color.WHITE);
				
		JPanel topPanel = new JPanel();
		topPanel.setLayout( new BoxLayout ( topPanel, BoxLayout.Y_AXIS ) );
		topPanel.add(topPanel1);
		topPanel.add(Box.createVerticalStrut(5));
		topPanel.add(topPanel2);
		topPanel.add(Box.createVerticalStrut(5));
		topPanel.add(topPanel3);
		
		topPanel.setMaximumSize( new Dimension ( Integer.MAX_VALUE, 
				(int)topPanel.getPreferredSize ().getHeight() ) );
		
		topPanel.setBackground(Color.WHITE);
		return topPanel;
	}
	
	/************************************************
	 * replica of createESTToolbar but to show sequences only
	 */
	private JPanel createSeqToolbar ( )
	{	
        // Button panel
		btnViewType = new JButton ("View Bases");
		btnViewType.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				toggleViewType();
			}
		});
		
		Dimension dim2 = new Dimension ( (int)(btnViewType.getPreferredSize().getWidth() * 1.5), 
											(int)btnViewType.getPreferredSize().getHeight() );
		menuHorzRatio = new JComboBox ();
		menuHorzRatio.setPreferredSize( dim2 );
		menuHorzRatio.setMaximumSize ( dim2 );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:1", 1 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:2", 2 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:3", 3 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:4", 4 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:5", 5 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:6", 6 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:7", 7 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:8", 8 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:9", 9 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:10", 10 ) );
		menuHorzRatio.setBackground(Color.WHITE);
		menuHorzRatio.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					MenuMapper selection = (MenuMapper) menuHorzRatio.getSelectedItem();
					changeBasesPerPixel(selection.asInt());
				} catch (Exception err) {
					ErrorReport.reportError(err);
				}
				revalidate(); 
			}
		});		
					
		JPanel topPanel3 = new JPanel();
		topPanel3.setLayout( new BoxLayout ( topPanel3, BoxLayout.X_AXIS ) );
		topPanel3.add( Box.createHorizontalStrut(5) );			
		topPanel3.add(btnViewType);
		topPanel3.add( Box.createHorizontalStrut(5) );
		topPanel3.add(menuHorzRatio);
		topPanel3.add( Box.createHorizontalStrut(5) );
		
		topPanel3.add( Box.createHorizontalGlue() );
		topPanel3.setBackground(Color.WHITE);
				
		JPanel topPanel = new JPanel();
		topPanel.setLayout( new BoxLayout ( topPanel, BoxLayout.Y_AXIS ) );
	
		topPanel.add(Box.createVerticalStrut(5));
		topPanel.add(topPanel3);
		
		topPanel.setMaximumSize( new Dimension ( Integer.MAX_VALUE, 
				(int)topPanel.getPreferredSize ().getHeight() ) );
		
		topPanel.setBackground(Color.WHITE);
		return topPanel;
	}
	
	public void changeESTSortOrder(int type) {
		menuViewType.setSelectedIndex(type);
		changeSortOrder(type);
		revalidate(); 
	}
	
	private void RemoveAfterCAPReplace(String sourceTitle)
	{
		JPaveFrame theFrame = (JPaveFrame)UIHelpers.findParentFrame(this);
		
		theFrame.removeMenuTreeNodeAfterReplace(sourceTitle);
		
	}
	
	// second row of pairwise -- things in here not used
	private JPanel createPairwiseToolbar ( boolean usesSelection)
	{
        // Button panel
		btnViewType = new JButton ("View Bases");
		btnViewType.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				toggleViewType();
			}
		});
		
		Dimension dim = new Dimension ( (int)(btnViewType.getPreferredSize().getWidth() * 3.5), 
											(int)btnViewType.getPreferredSize().getHeight() );
		menuShowCoding = new JComboBox ();
		menuShowCoding.setPreferredSize( dim );
		menuShowCoding.setMaximumSize ( dim );
		menuShowCoding.addItem( new MenuMapper ( "Don't show coding region", SHOW_CODING_NONE ) );
		menuShowCoding.addItem( new MenuMapper ( "Show longest ORF", SHOW_CODING_ORF ) );
		menuShowCoding.addItem( new MenuMapper ( "Show UniProt aligned ORF", SHOW_CODING_PROTEIN ) );		
		menuShowCoding.setBackground(Color.WHITE);
		menuShowCoding.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				MenuMapper selection = (MenuMapper) menuShowCoding.getSelectedItem();
				changeShowCoding(selection.asInt());
			}
		});		
		
		Dimension dim2 = new Dimension ( (int)(btnViewType.getPreferredSize().getWidth() * 1.5), 
											(int)btnViewType.getPreferredSize().getHeight() );
		menuHorzRatio = new JComboBox ();
		menuHorzRatio.setPreferredSize( dim2 );
		menuHorzRatio.setMaximumSize ( dim2 );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:1", 1 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:2", 2 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:3", 3 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:4", 4 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:5", 5 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:6", 6 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:7", 7 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:8", 8 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:9", 9 ) );
		menuHorzRatio.addItem( new MenuMapper ( "Horz. Ratio = 1:10", 10 ) );
		menuHorzRatio.setBackground(Color.WHITE);
		menuHorzRatio.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					MenuMapper selection = (MenuMapper) menuHorzRatio.getSelectedItem();
					changeBasesPerPixel(selection.asInt());
				} catch (Exception err) {
					ErrorReport.reportError(err);
				}
				revalidate(); 
			}
		});		
		
		JButton btnClusterSelectNone = null;
		JButton btnClusterSelectAll = null;
		
		if(usesSelection) {
			btnClusterSelectAll = new JButton ( "Select All" );
			btnClusterSelectAll.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					selectAll();
				}
			});
		
			btnClusterSelectNone = new JButton ( "Unselect All" ); 
			btnClusterSelectNone.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					selectNone();
				}
			});
		}

		// Top panel with buttons and drop-down
		JPanel topPanel = new JPanel ( );
		topPanel.setLayout( new BoxLayout ( topPanel, BoxLayout.X_AXIS ) );
		topPanel.add( Box.createHorizontalStrut(5) );
		if(usesSelection) {
			topPanel.add( btnClusterSelectAll );
			topPanel.add( Box.createHorizontalStrut(5) );
			topPanel.add( btnClusterSelectNone );
			topPanel.add( Box.createHorizontalStrut(5) );
		}
		topPanel.add( btnViewType );
		topPanel.add( Box.createHorizontalStrut(5) );
		topPanel.add( menuHorzRatio );
        topPanel.add( Box.createHorizontalStrut(5) );            
        topPanel.add( Box.createHorizontalStrut(5) );
	    topPanel.setMaximumSize( new Dimension ( Integer.MAX_VALUE, 
	    		(int)topPanel.getPreferredSize().getHeight() ) );
	    topPanel.setBackground(Color.WHITE);
	    
	    return topPanel;
	}
	
	private void changeShowCoding ( int nNewVal )
	{
		Iterator<MainAlignPanel> iter = listOfAlignmentPanels.iterator();		
		while ( iter.hasNext() )
		{
			MainAlignPanel curPanel = (MainAlignPanel)iter.next();
			changeShowCoding ( curPanel, nNewVal );
		}			
	}
	
	static void changeShowCoding ( MainAlignPanel panel, int nNewVal )
	{
		boolean bShowProtein = nNewVal == SHOW_CODING_PROTEIN;
		boolean bShowORF = nNewVal == SHOW_CODING_ORF;
	
		panel.setShowORFCoding ( bShowORF );
		panel.setShowProteinCoding ( bShowProtein );
	}
	
	private void exportToFASTA ( PrintStream seqStream, PrintStream qualStream, boolean bConsensusOnly,
			boolean hasSelection) throws Exception 
	{
		// Have each contig panel add its selected ESTs to the files
		Iterator<MainAlignPanel> iter = listOfAlignmentPanels.iterator();		
		while ( iter.hasNext() )
		{
			MainAlignPanel curPanel = iter.next();
			
			if (hasSelection) {
				if ( bConsensusOnly )
					curPanel.addSelectedConsensusToFASTA( seqStream, qualStream );
				else
					curPanel.addSelectedSequencesToFASTA ( seqStream, qualStream);
			}
			else { 
				if ( bConsensusOnly )
					curPanel.addConsensusToFASTA( seqStream, qualStream );
				else
					curPanel.addAllSequencesToFASTA ( seqStream, qualStream );
			}
		}		
	}
	
	private void messageDialog (String msg)
	{
		final JDialog dlg = new JDialog ( UIHelpers.findParentFrame( this ), "sTCW message" );
			
		JButton btnOK = new JButton ( "OK" );	
		btnOK.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				dlg.dispose();
			}
		});	
		
		JPanel bottomPanel = new JPanel ( new FlowLayout () );
		bottomPanel.add( btnOK );
		
		// Install everything into the dialog's content pane
		Container content = dlg.getContentPane();
		content.setLayout( new BorderLayout () );
		content.add( new JLabel (msg), BorderLayout.NORTH );
		content.add( bottomPanel, BorderLayout.SOUTH );	
		
		// Show the dialog
		dlg.pack();
		dlg.setModal(true);
        UIHelpers.centerScreen ( dlg );
		dlg.setVisible(true);
	}
	
	private boolean poseExportDialog ( )
	{
		final JDialog dlg = new JDialog ( UIHelpers.findParentFrame( this ), "Save to File Options" );
		
		// Create radio buttons
		final JRadioButton rdExportConsensus;
		JRadioButton rdExportSequence;
		rdExportSequence = new JRadioButton ( "Selected EST sequences" );
		rdExportConsensus = new JRadioButton ( "Selected consensus sequences" );
		
		// Add to the group
		final ButtonGroup radioGroup = new ButtonGroup ();
		radioGroup.add( rdExportSequence );
		radioGroup.add( rdExportConsensus );
		radioGroup.setSelected ( rdExportSequence.getModel(), true );

		// Add to the options panel
		JPanel optionPanel = new JPanel ();
		optionPanel.setLayout( new BoxLayout ( optionPanel, BoxLayout.Y_AXIS ) );
		optionPanel.add( rdExportSequence );
		optionPanel.add( rdExportConsensus );
		
		// Button panel with OK and cancel		
		JButton btnOK = new JButton ( "OK" );
		btnOK.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				bDoExport = true;
				bExportConsensus = radioGroup.getSelection() == rdExportConsensus.getModel();
				dlg.dispose();
			}
		});	
		
		JButton btnCanel = new JButton ( "Cancel" );
		btnCanel.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				bDoExport = false;
				dlg.dispose();
			}
		});
		
		JPanel bottomPanel = new JPanel ( new FlowLayout () );
		bottomPanel.add( btnCanel );
		bottomPanel.add( btnOK );
		
		// Install everything into the dialog's content pane
		Container content = dlg.getContentPane();
		content.setLayout( new BorderLayout () );
		content.add( new JLabel ("What do you want to save?"), BorderLayout.NORTH );
		content.add( optionPanel, BorderLayout.CENTER );
		content.add( bottomPanel, BorderLayout.SOUTH );	
		
		// Show the dialog
		dlg.pack();
		dlg.setModal(true);
        UIHelpers.centerScreen ( dlg );
		dlg.setVisible(true);
		return bDoExport;
	}
	// Save To File from contig display
	private void handleExport ( )
	{
		try
		{
			int b = getShowBuriedMenuSelection();
			if (b==1) {
				messageDialog(" Save aborted. Use Show Buried (Detail) to export all ESTs in Contig ");
				return;
			}
			// First determine if anything has been selected 
			boolean hasSelection = false;
			for (MainAlignPanel panel : listOfAlignmentPanels)
				hasSelection |=	panel.hasSelection();
			
			if ( ! (listOfAlignmentPanels.firstElement() instanceof MainContigAlignPanel) )
			{
				// Just export what is selected
				bExportConsensus = false;
				bDoExport = true;
			}
			else
				//	Display a dialog to determine what to export
				// TODO -- bExportConsensus doesn't make sense for multiple aligned consensus sequences
				bDoExport = poseExportDialog ();	
			
			if ( bDoExport )	
			{
				JPaveFrame theFrame = (JPaveFrame)UIHelpers.findParentFrame( this );
				
				File seqFile = null;
				if ( theFrame.getApplet() == null )
				{
					JFileChooser chooser = new JFileChooser (theFrame.lastSaveFilePath); 
					chooser.setDialogTitle( "Select file" );
					if ( chooser.showSaveDialog( this ) == JFileChooser.APPROVE_OPTION )		
						seqFile = chooser.getSelectedFile();
				}
					
				if ( seqFile != null )
				{			
					String qualSuffix = ".est.qual";
					if (bExportConsensus) qualSuffix = ".unitran.qual"; 
					File qualFile = new File ( seqFile.getParentFile(), seqFile.getName() + qualSuffix );
					
					String seqSuffix = ".est.seq";
					if (bExportConsensus) seqSuffix = ".unitran.seq"; 
					seqFile = new File(seqFile.getParentFile(), seqFile.getName() + seqSuffix); 
					
					PrintStream seqStream = new PrintStream ( new FileOutputStream( seqFile ) );
					PrintStream qualStream = new PrintStream ( new FileOutputStream( qualFile ) );
					
					exportToFASTA ( seqStream, qualStream, bExportConsensus, hasSelection );

					seqStream.close();
					if ( qualStream != null )
						qualStream.close();	
					
					theFrame.lastSaveFilePath = seqFile.getPath(); 
				}
			}
		} 
		catch (Exception err)
		{
			ErrorReport.reportError(err, "Handle export error");
		}
	}

	public Vector<String> getContigIDs ( )
	{
		Vector<String> out = new Vector<String>();
		
		for (MainAlignPanel curPanel : listOfAlignmentPanels) {	
			out.addAll(curPanel.getContigIDs());
		}	
		return out;
	}
	
	public TreeSet<String> getSelectedContigIDs ( )
	{
		// Get the selection for each panel
		TreeSet<String> selection = new TreeSet<String> ();
		
		for (MainAlignPanel curPanel : listOfAlignmentPanels) {	
			curPanel.getSelectedContigIDs( selection );
		}			
		return selection;
	}
	
	public void setSelectedContigs ( TreeSet<String> selectedIDs )
	{
		// Set the selection for each panel
		for (MainAlignPanel curPanel : listOfAlignmentPanels) {	
			curPanel.setSelectedContigIDs( selectedIDs );
		}		
	}
	
	public void setSelectedClones(String strNamePattern) 
	{
		for (MainAlignPanel curPanel : listOfAlignmentPanels)
			curPanel.selectMatchingSequences(strNamePattern);
	}
	
	private void handleClick (MouseEvent e)
	{
		// Convert to view relative coordinates
		int viewX = (int) (e.getX() + scroller.getViewport().getViewPosition().getX());
		int viewY = (int) (e.getY() + scroller.getViewport().getViewPosition().getY());
		
		// Go through  all the panels and see which one was clicked on:
		Iterator<MainAlignPanel> iter = listOfAlignmentPanels.iterator();
		while ( iter.hasNext() )
		{
			// Get the panel and convert to panel relative coordinates
			MainAlignPanel curPanel = (MainAlignPanel)iter.next();
			int nPanelX = viewX - curPanel.getX();
			int nPanelY = viewY - curPanel.getY();
			
			if ( curPanel.contains( nPanelX, nPanelY ) )
				// Click is in current panel, let the object handle it
				curPanel.handleClick( e, new Point( nPanelX, nPanelY ) );
			else
				// Clear all selections in the panel unless shift or control are down
				if ( !e.isShiftDown() && !e.isControlDown() )
					curPanel.selectNone();
		}
	}

	private void alignSelectedESTs ( String strProgramToUse ) 
	{
		try
		{
			JPaveFrame theFrame = (JPaveFrame)UIHelpers.findParentFrame( this );
			
			// Have each contig panel add its selected ESTs to the set
			TreeSet<String> selectedESTs = new TreeSet<String> ();
			Iterator<MainAlignPanel> iter = listOfAlignmentPanels.iterator();		
			while ( iter.hasNext() ) {
				MainAlignPanel curPanel = iter.next();
				curPanel.addSelectedSequencesToSet ( selectedESTs );			
			}
			
			// Make sure the user has selected something 
			if ( selectedESTs.size() == 0 ) {
				JOptionPane.showMessageDialog( 	theFrame, 
							"Please select the ESTs you wish to align first.",
							"No ESTs Selected",
							JOptionPane.PLAIN_MESSAGE );
				return;
			}
			
			// Open a new tab with the CAP options
			CAP3orPhrapTab tab = new CAP3orPhrapTab ( 
					theFrame, 
					nRecordNum, 
					strProgramToUse, 
					selectedESTs,
					getContigIDs());
	
			theFrame.addTab(tab.getAlignName(), tab);
			
			// Add new CAP3/PHRAP node to menu tree 
			String nodeName = "#" + (nRecordNum+1) + ": "+ tab.getAlignName();
			String toolTipText = selectedESTs.size() + " ESTs";
			theFrame.addNewMenuTreeNode(JPaveFrame.ShowAllContigs, nodeName, tab, toolTipText);
		}
		catch ( Throwable err ) { ErrorReport.reportError(err); }
	}
	
	private void setShowSNPs ( boolean bShowSNPS )
	{		
		// Notify all sub-panels
		Iterator<MainAlignPanel> iter = listOfAlignmentPanels.iterator();
		while ( iter.hasNext() )
		{
			MainContigAlignPanel curPanel = (MainContigAlignPanel)iter.next ();
			curPanel.setShowSNPs ( bShowSNPS );
		}
	}
	
	public void setShowBuriedAllPanels (boolean hasNoAssembly )
	{	
		// Notify all sub-panels
		if (hasNoAssembly) return;
		Iterator<MainAlignPanel> iter = listOfAlignmentPanels.iterator();
		while ( iter.hasNext() )
		{
			MainContigAlignPanel curPanel = (MainContigAlignPanel)iter.next ();
			curPanel.setShowBuried ( menuShowBuried.getSelectedIndex() );
		}
	}

	private void selectAll ( )
	{
		// Go through  all the panels 
		Iterator<MainAlignPanel> iter = listOfAlignmentPanels.iterator();
		
		while ( iter.hasNext() )
		{
			MainAlignPanel curPanel = iter.next();
			curPanel.selectAll();
		}		
	}
	
	private void selectNone ( )
	{
		// Go through  all the panels 
		Iterator<MainAlignPanel> iter = listOfAlignmentPanels.iterator();
		
		while ( iter.hasNext() )
		{
			MainAlignPanel curPanel = iter.next();
			curPanel.selectNone();
		}	
	}
	
	private void fitBasesPerPixel ( )
	{
		int nViewPortWidth = (int)scroller.getViewport().getBounds().getWidth();
		int nOptBasesPer = 1;
		int nCurAvailable;
		int nCurBasesPer;
		
		// Notify all sub-panels
		Iterator<MainAlignPanel> iter = listOfAlignmentPanels.iterator();
		while ( iter.hasNext() )
		{
			MainAlignPanel curPanel = iter.next ();
			nCurAvailable = nViewPortWidth - (int)curPanel.getGraphicalDeadWidth();
			nCurBasesPer = (int) Math.ceil( curPanel.getTotalBases () / (double)nCurAvailable );
			nOptBasesPer = Math.max( nOptBasesPer, nCurBasesPer );
		}		
		
		// Set the optimum value
		nOptBasesPer = Math.min( nOptBasesPer, 10 );
		menuHorzRatio.setSelectedIndex ( nOptBasesPer - 1 );
	}
	
	private void changeBasesPerPixel ( int n ) throws Exception
	{
		// Notify all sub-panels
		Iterator<MainAlignPanel> iter = listOfAlignmentPanels.iterator();
		while ( iter.hasNext() )
		{
			MainAlignPanel curPanel = iter.next ();
			curPanel.setBasesPerPixel ( n );
		}		
		scroller.getViewport().getView().setVisible( true );
	}
	
	private void toggleViewType ( )
	{
		// Toggle the mode
		if ( nViewType == MainAlignPanel.GRAPHICMODE )
			nViewType = MainAlignPanel.TEXTMODE;
		else
			nViewType = MainAlignPanel.GRAPHICMODE;
		
		setViewType();
	}
	
	private void setViewType ()
	{
		// Notify all sub-panels
		Iterator<MainAlignPanel> iter = listOfAlignmentPanels.iterator();

		if ( nViewType == MainAlignPanel.GRAPHICMODE )
		{
			btnViewType.setText( "View Bases" );
			menuHorzRatio.setVisible ( true );
			while ( iter.hasNext() )
			{
				MainAlignPanel curPanel = iter.next ();
				curPanel.setDrawMode (MainAlignPanel.GRAPHICMODE);	
			}
		}
		else
		{
			btnViewType.setText( "View Graphic" );		
			menuHorzRatio.setVisible ( false );
			while ( iter.hasNext() )
			{
				MainAlignPanel curPanel = iter.next ();
				curPanel.setDrawMode (MainAlignPanel.TEXTMODE);	
			}
		}	
				
		scroller.setViewportView( scroller.getViewport().getView() );		
	}
	
	private void changeSortOrder ( int nNewOrder )
	{
		// Notify all sub-panels
		Iterator<MainAlignPanel> iter = listOfAlignmentPanels.iterator();
		while ( iter.hasNext() )
		{
			MainContigAlignPanel curPanel = (MainContigAlignPanel)iter.next ();
			curPanel.changeSortOrder ( nNewOrder );
			curPanel.refreshPanels();
		}		
	}
	
    public void ShowReplaceBtn(boolean show, boolean hasAssembly)
    {
    		if (hasAssembly)
    			btnReplace.setVisible(show);
    }
    
    //Gets called when user clicks Next>> or <<Prev
    public void setShowBuriedMenuSelection(int selection, boolean hasAssembly) 
    { 
    		if (hasAssembly)
    			menuShowBuried.setSelectedIndex(selection); 
    }
	public int getShowBuriedMenuSelection() {
		//some times synchronization too slow for 
		//menu to be loaded, so return "Hide Buried"
		if(menuShowBuried == null) return 0;
		return menuShowBuried.getSelectedIndex(); 
	}
	
	public void setHorzRatioMenuSelection(int selection) { menuHorzRatio.setSelectedIndex(selection); }
	public int getHorzRatioMenuSelection() { return menuHorzRatio.getSelectedIndex(); }
	
	public void setShowCodingMenuSelection(int selection) { menuShowCoding.setSelectedIndex(selection); }
	public int getShowCodingMenuSelection() { return menuShowCoding.getSelectedIndex(); }
	
	public void setViewTypeMenuSelection(int selection) { menuViewType.setSelectedIndex(selection); }
	public int getViewTypeMenuSelection() { return menuViewType.getSelectedIndex(); }
	
	/**
	 * Called before new contig loaded from either a next>> or <<prev click
	 */
	public int [] getDisplaySettings() {
		int [] retVal = new int[6];
		//Value used for both contig and contig pair listings
		if (menuShowCoding != null) 
			retVal[0] = menuShowCoding.getSelectedIndex();
		if(menuViewType != null) 
			retVal[1] = menuViewType.getSelectedIndex();
		if(menuShowBuried != null)
			retVal[2] = menuShowBuried.getSelectedIndex();
		if(menuHorzRatio != null)
			retVal[3] = menuHorzRatio.getSelectedIndex();
		retVal[4] = nViewType;
		if(btnViewSNPs != null && btnViewSNPs.getText().equals("Hide SNPs"))
			retVal[5] = 1;
		else
			retVal[5] = 0;
		return retVal;
	}

	/**
	 * Called after new contig loaded on a next, prev click
	 * @param settings previous view settings
	 */
	public void applyDisplaySettings(int [] settings) {
		if(settings == null) {
			return;
		}
		if (menuShowCoding != null) 
			menuShowCoding.setSelectedIndex(settings[0]);
		if(menuViewType != null)
			menuViewType.setSelectedIndex(settings[1]);
		if(menuShowBuried != null)
			menuShowBuried.setSelectedIndex(0);
		if(menuHorzRatio != null)
			menuHorzRatio.setSelectedIndex(settings[3]);
		nViewType = settings[4];
		setViewType();
		if(btnViewSNPs != null && btnViewSNPs.getText().equals("Show SNPs") && settings[5] == 1)
			btnViewSNPs.doClick();
	}
	
	static final private int GAP_WIDTH = 10;
	static final int nTopGap = GAP_WIDTH;
	static final int nBottomGap = GAP_WIDTH / 2;
	static final int nSideGaps = GAP_WIDTH; 
	
	static final private int SHOW_CODING_NONE = 0;
	static final private int SHOW_CODING_ORF = 3;
	static final private int SHOW_CODING_PROTEIN = 5;
	
    static private boolean isPair = false;
	
    private GlobalSettings theSettings = null;
    
	private boolean bDoExport = false;
	private boolean bExportConsensus = false;
	private int nRecordNum;
	private int parentTabID;

	private int nViewType = MainAlignPanel.GRAPHICMODE;
	
	private JButton btnViewSNPs = null;

	private JComboBox menuShowBuried = null;
    private JButton btnCAP3 = null;
    private JButton btnPHRAP = null;
    private JButton btnViewType = null;
    private JButton btnReplace = null;
	private JComboBox menuShowCoding = null;
	private JComboBox menuHorzRatio = null;
	private JComboBox menuViewType = null;
	private JScrollPane scroller = null;
	private Vector<MainAlignPanel> listOfAlignmentPanels = null;
    
    private static final long serialVersionUID = 1;	
}
